/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kairos.motion 
{
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.display.Scene;
	
	import com.bourre.commands.ASyncCommand;
	import com.bourre.error.IllegalArgumentException;
	import com.bourre.error.NoSuchElementException;
	import com.bourre.structures.Range;
	import com.bourre.transitions.TickListener;
	import com.kairos.log.KairosDebug;	

	/**
	 * <code>AdvancedMovieClipRT</code> is a concret implementation
	 * of the <code>AdvancedAbstractAnimation</code> class which
	 * handle the animation of a <code>MovieClip</code> object.
	 * <p>
	 * The <code>AdvancedMovieClipRT</code> class add controls over
	 * the frame label and scene functionnalities provided by the
	 * <code>MovieClip</code> class.
	 * </p>
	 * @author 	Cédric Néhémie
	 */
	public class AdvancedMovieClipRT extends AbstractAdvancedAnimation implements TickListener, ASyncCommand 
	{
		private static const __NUM_FRAMES_LABEL__ : String = "__NUM_FRAMES_LABEL__";
		
		private var __oLabels : Object;
		private var __oScenes : Object;
		
		/**
		 * A reference to the <code>MovieClip</code> instance handled by this object.
		 */
		protected var _mcTarget : MovieClip;
	
		/**
		 * Creates a new <code>AdvancedMovieClipRT</code> object which
		 * handle the passed-in <code>MovieClip</code> instance. 
		 * 
		 * @param   mc		  movie clip to be handled   
		 * @param   fps 	  speed of the animation in frame per second
		 * @param	loopCount number of loops before the animation stop
		 * @param	range 	  frames range within which the animation run
		 * @param	r		  <code>true</code> to make the movieclip playing in
		 * 					  the reversed arrow of time  
		 * @example	Creating an AdvancedMovieClipRT with different arguments configuration :
		 * <listing>var amc : AdvancedMovieClipRT = new AdvancedMovieClipRT ( myMc, 40 );</listing>
		 * <listing>var amc : AdvancedMovieClipRT = new AdvancedMovieClipRT ( myMc, 40, 10);</listing>
		 * <listing>var amc : AdvancedMovieClipRT = new AdvancedMovieClipRT ( myMC, 40, 10, new Range( 5, 10 ) );</listing>
		 * <listing>var amc : AdvancedMovieClipRT = new AdvancedMovieClipRT ( myMC, 40, 10, new Range( 5, 10 ), true );</listing>
		 */
		public function AdvancedMovieClipRT( mc : MovieClip, 
											 fps : uint = DEFAULT_FRAMERATE,
											 loopCount : uint = 0,
											 range : Range = null,
											 r : Boolean = false ) 
		{	
			super( fps, loopCount, range, r );
			setMovieClip( mc );
			setRange( range );
			reset();
		}
		
		
		/**
		 * /**
		 * Defines the <code>MovieClip</code> instance to be handled by this 
		 * <code>AdvancedMovieClipRT</code> object.
		 * 
		 * @param   mc	movie clip to be handled  
		 * @throws 	<code>IllegalArgumentException</code> — The passed-in movie
		 * 			clip is null.
		 */
		public function setMovieClip ( mc : MovieClip = null ) : void
		{
			if( !mc )
			{
				var msg : String = "Passed in movieclip in " + this + ".setMovieClip() is null";
				
				KairosDebug.ERROR( msg );
				throw new IllegalArgumentException( msg );
			}
			
			_mcTarget = mc;
			_nTotalFrames = _mcTarget.totalFrames;
			_nFrame = _oStrategy.getInitialFrame( _nTotalFrames );
			_mcTarget.stop();
			
			__registerScenes ( _mcTarget );
		}
		
		/**
		 * Registers scenes and label for the passed-in movie in order
		 * to optimize access while moving the playhead.
		 * @private
		 */
		private function __registerScenes ( mc : MovieClip ) : void
		{
			__oScenes = {};
			
			var l : Number = mc.scenes.length;
			while( --l -(-1) )
			{
				var sc : Scene = mc.scenes[ l ] as Scene;
				var labels : Array = sc.labels;
				var l2 : Number = labels.length;
				var o : Object = {};
 				o[__NUM_FRAMES_LABEL__] = sc.numFrames;	
				while( --l2 -(-1) )
				{
					var fl : FrameLabel = labels[ l2 ] as FrameLabel;
					o[ fl.name ] = fl.frame;
				}
				__oScenes[ sc.name ] = o;
			}
			__oLabels = __oScenes [ mc.currentScene.name ];		
		}
		
		/**
		 * Returns the current <code>MovieClip</code> object
		 * handled by this object.
		 * 
		 * @return  the movie clip handled by this object
		 */
		public function getMovieClip () : MovieClip 
		{
			return _mcTarget;
		}
		
		/**
		 * Brings the playhead to the specified frame of the movie clip
		 * and start it there. 
		 * 
		 * @param	f	  a number representing the frame number,
		 * 				  or a string representing the label of the frame,
		 * 				  to which the playhead is sent
		 * @param	scene the name of the scene to play. This parameter is optional
		 * 
		 * @throws 	<code>IllegalArgumentException</code> — The method was called with
		 * 			an invalid argument.
		 * @throws 	<code>NoSuchElementException</code> — The specified scene doesn't
		 * 			exist in the movie clip.
		 */
		public function gotoAndPlay ( f : *, scene : String = null ) : void
		{
			if( f is uint )	
				_gotoFrame ( f as uint );
			else if ( f is String ) 
				_gotoLabel ( f as String );
			else 
			{
				var msg : String = this + ".gotoAndPlay() called with an invalid argument : " + f;
				
				KairosDebug.ERROR( msg );				
				throw new IllegalArgumentException ( msg );
				
			}
			if( scene ) gotoScene( scene );
			
			super.start();
		}
		
		/**
		 * Brings the playhead to the specified frame of the movie clip
		 * and stops it there. 
		 * 
		 * @param	f	  a number representing the frame number,
		 * 				  or a string representing the label of the frame,
		 * 				  to which the playhead is sent
		 * @param	scene the name of the scene to play. This parameter is optional
		 * 
		 * @throws 	<code>IllegalArgumentException</code> — The method was called with
		 * 			an invalid argument.
		 * @throws 	<code>NoSuchElementException</code> — The specified scene doesn't 
		 * 			exist in the movie clip.
		 */
		public function gotoAndStop ( f : *, scene : String = null ) : void
		{
			if( f is uint )	
				_gotoFrame ( f as uint );
			else if ( f is String ) 
				_gotoLabel ( f as String );
			else 
			{
				var msg : String = this + ".gotoAndStop() called with an invalid argument : " + f;
				
				KairosDebug.ERROR( msg );				
				throw new IllegalArgumentException ( msg );
			}
			if( scene ) gotoScene( scene );
			super.stop();;
		}
		
		/**
		 * Brings the playhead to the specified scene in the MovieClip.
		 * <p>
		 * The frame, onto which the playhead is, will be kept during
		 * the scene changes, except if the frame doesn't exist in the
		 * destination scene.
		 * </p> 
		 * @param	scene the name of the scene to play.
		 * @throws 	<code>NoSuchElementException</code> — The specified
		 * 			scene doesn't exist in the movie clip. 
		 */
		public function gotoScene ( scene : String ) : void
		{
			if( !__oScenes[ scene ] )
			{
				_mcTarget.gotoAndStop( _nFrame, scene );
				_nTotalFrames = __oScenes[ scene ][ "__NUM_FRAMES_LABEL__" ];
				__oLabels = __oScenes[ scene ];
			}
			else
			{
				var msg : String = "You are trying to go to an undefined scene '" + scene + "' in " + this;
				KairosDebug.ERROR( msg );
				throw new NoSuchElementException ( msg ); 
			}
		}
		
		/**
		 * 
		 * @param f
		 * 
		 */
		protected function _gotoFrame ( f : uint ) : void
		{
			if( f == 0 ) f = 1;
			
			_nFrame = f - _nRangeStart;
			_frameChanged ( f );
			
		}
		/**
		 * 
		 * @param f
		 * 
		 */
		protected function _gotoLabel ( l : String ) : void
		{
			if( __oLabels[ l ] != undefined )
				_gotoFrame ( __oLabels[ l ] );
			else
				KairosDebug.ERROR( "Trying to access label '" + l + "' that doesn't exist in " + this );
		}
		/**
		 * Makes the composed <code>MovieClip</code> visible on the screen.
		 */
		public function show () : void
		{
			_mcTarget.visible = true;
		}
		
		/**
		 * Makes the composed <code>MovieClip</code> invisible.
		 */
		public function hide () : void
		{
			_mcTarget.visible = false;
		} 
		
		/**
		 * Called when frame changed according to internal computation.
		 * Override the <code>AbstractAnimation</code> implementation.
		 * 
		 * @param	f	the new frame number
		 */
		protected override function _frameChanged ( f : Number ) : void
		{ 
		 	_mcTarget.gotoAndStop( f );
		}
	}
}